#include "XMLFileLoader.h"


XMLFileLoader::XMLFileLoader(){
	scene = NULL;
}


XMLFileLoader::~XMLFileLoader(){
}

bool XMLFileLoader::loadFileToScene(const char* _f){
	if(isVerbose)printf("Starting to load %s\n", _f);
	if (scene == NULL){
		printf("No scene attached\n");
		return false;
	}
	//attempt to open xml file
	xml_document<> doc;
	std::ifstream file(_f);
	if (!file.is_open()){
		printf("Failed to open XML file\n");
		return false;
	}
	std::stringstream buffer;
	buffer << file.rdbuf();
	file.close();
	std::string content(buffer.str());
	
	//attempt to parse the xml file
	doc.parse<0>(&content[0]);
	xml_node<> *theRest = doc.first_node("render");
	//get the render settings
	xml_node<> *renderSettings = theRest->first_node("renderer");
	processRenderSettings(renderSettings);
	//get the shaders
	xml_node<> *shaders = theRest->first_node("shaders");
	processShaders(shaders);
	//get the frames
	xml_node<> *frames = theRest->first_node("frames");
	processFrames(frames);
	if (isVerbose)printf("Finished loading the file\n");
	//access the Scene and fill it
	return true;
}

bool XMLFileLoader::processRenderSettings(xml_node<> *_n){
	//if _n is null, then all the render settings will be their defaults
	if (_n == NULL){
		return false;
	}
	if (isVerbose)printf("\nProcessing renderer\n");
	for (xml_node<> *node = _n->first_node(); node; node = node->next_sibling()){
		string nodeName = node->name();
		if (nodeName == "image"){
			//get the width and height
			xml_attribute<> *pAttr = node->first_attribute("width");
			int width = stoi(pAttr->value());
			pAttr = node->first_attribute("height");
			int height = stoi(pAttr->value());
			pAttr = node->first_attribute("layers");
			int layers = 1;
			if (pAttr != NULL){
				layers = stoi(pAttr->value());
			}
			//store the image dimensions
			if (isVerbose)printf("Image--width: %i, height: %i\n", width, height);
			scene->imgSize(width, height, layers);
		}
		else if (nodeName == "maxbounces"){
			int bounces = stoi(node->value());
			//store the max bounces
			if (isVerbose)printf("Max bounces: %i\n", bounces);
			scene->maxBounces(bounces);
		}
		else if (nodeName == "aamode"){
			int aamode = stoi(node->value());
			//store the aa mode
			if (isVerbose)printf("AAmode: %i\n", aamode);
			scene->AAmode(aamode);
		}
		else if (nodeName == "aaminsamples"){
			int minsamples = stoi(node->value());
			//store the aa min samples
			if (isVerbose)printf("AA Min Samples: %i\n", minsamples);
			scene->AAminSamples(minsamples);
		}
		else if (nodeName == "aamaxsamples"){
			int maxsamples = stoi(node->value());
			//store the aa max samples
			if (isVerbose)printf("AA Max Samples: %i\n", maxsamples);
			scene->AAmaxSamples(maxsamples);
		}
		else if (nodeName == "bgcolor"){
			vec4 bgcolor;
			getVec4(node, bgcolor);
			//store the background color
			if (isVerbose)printf("BGcolor: (%f, %f, %f)\n", bgcolor.r, bgcolor.g, bgcolor.b);
			scene->backgroundColor(bgcolor);
		}
		else if (nodeName == "bvhnodesize"){
			int bvhtargetsize = stoi(node->value());
			//TODO: store the bvh node size
			if (isVerbose)printf("BVH node size: %i\n", bvhtargetsize);
			
		}
		else if (nodeName == "output"){
			//TODO: get the output file info
			xml_node<> *outNode = node->first_node("file");
			if (outNode != NULL){
				xml_attribute<> *pAttr = outNode->first_attribute("name");
				outFile = pAttr->value();
			}
			//TODO: store the output file info
			if (isVerbose)printf("Output node: %s\n", outFile);
		}
	}

	return true;
}

bool XMLFileLoader::processShaders(xml_node<> *_n){
	//if _n is null, then there will be no shaders
	if (_n == NULL){
		return false;
	}
	if (isVerbose)printf("\nProcessing Shaders\n");
	//for each shader collection
	for (xml_node<> *node = _n->first_node(); node; node = node->next_sibling()){
		processShaderCollection(node);
	}
	
	return true;
}

bool XMLFileLoader::processShaderCollection(xml_node<> *_n){
	if (_n == NULL){
		printf("No shader collection\n");
		return false;
	}
	xml_attribute<> *pAttr = _n->first_attribute("name");
	string shaderName = pAttr->value();
	scene->startShaderCollection(shaderName);
	//for each subshader
	for (xml_node<> *node = _n->first_node(); node; node = node->next_sibling()){
		string nodeName = node->name();
		pAttr = node->first_attribute("amount");
		float amount = stof(pAttr->value());

		pAttr = node->first_attribute("type");
		string shaderType = pAttr->value();
		if (shaderType == "specular"){
			//get the shininess
			xml_node<> *shinyNode = node->first_node("shininess");
			float shininess = stof(shinyNode->value());
			//get the color
			xml_node<> *colorNode = node->first_node("color");
			vec4 specColor;
			getVec4(colorNode, specColor);

			//add specular shader
			if (isVerbose)printf("Specular shader: Amount: %f, Shine: %i\n", amount, shininess);
			if (isVerbose)printf("color: (%f, %f, %f)\n", specColor.r, specColor.g, specColor.b);
			scene->addSpecularShader(amount, specColor, shininess);
		}
		else if (shaderType == "diffuse"){
			//get the color
			xml_node<> *colorNode = node->first_node("color");
			vec4 diffColor;
			getVec4(colorNode, diffColor);

			//add diffuse shader
			if (isVerbose)printf("Diffuse shader: Amount: %f\n", amount);
			if (isVerbose)printf("color: (%f, %f, %f)\n", diffColor.r, diffColor.g, diffColor.b);
			scene->addDiffuseShader(amount, diffColor);
		}
		else if (shaderType == "reflective"){
			//get the color
			xml_node<> *colorNode = node->first_node("color");
			vec4 reflColor;
			getVec4(colorNode, reflColor);

			//add reflective shader
			if (isVerbose)printf("Reflective shader: Amount: %f\n", amount);
			if (isVerbose)printf("color: (%f, %f, %f)\n", reflColor.r, reflColor.g, reflColor.b);
			scene->addReflectiveShader(amount, reflColor);
		}
		else if (shaderType == "refractive"){
			//get the index of refraction
			xml_node<> *IORnode = node->first_node("IOR");
			float IOR = stof(IORnode->value());
			//add refractive shader
			if (isVerbose)printf("Refractive shader: Amount: %f, IOR: %f\n", amount, IOR);
			scene->addRefractiveShader(amount, IOR);
		}
	}
	scene->endShaderCollection();
	return true;
}

bool XMLFileLoader::processFrames(xml_node<> *_n){
	//if _n is null, then there will be no geometry
	if (_n == NULL){
		return false;
	}
	if (isVerbose)printf("\nProcessing frames\n");
	int frameNum = 0;
	//for each frame
	for (xml_node<> *node = _n->first_node(); node; node = node->next_sibling()){
		//clear out the existing geometry, lights, and cameras in the scene
		scene->clear();

		//add the geometry, lights, and camera to the scene
		processFrame(node);

		//render the image
		scene->startRender();

		//if they specified a file output
		string finalOutFile = outFile + to_string(frameNum) + ".png";
		scene->saveLayerToFile(finalOutFile, 0);
		frameNum++;
	}
	return true;
}

bool XMLFileLoader::processFrame(xml_node<> *_n){
	if (_n == NULL){
		printf("No frame\n");
		return false;
	}
	//get the camera
	xml_node<> *camNode = _n->first_node("camera");
	processCamera(camNode);
	//get the geometry
	xml_node<> *shapesNode = _n->first_node("shapes");
	processShapes(shapesNode);
	//get the lights
	xml_node<> *lightsNode = _n->first_node("lights");
	processLights(lightsNode);
	return true;
}

bool XMLFileLoader::processCamera(xml_node<> *_n){
	if (_n == NULL){
		printf("No camera\n");
		return false;
	}
	//get the fov
	xml_attribute<> *pAttr = _n->first_attribute("fov");
	float fov = stof(pAttr->value());
	//get the dimensions
	pAttr = _n->first_attribute("width");
	int width = stoi(pAttr->value());
	pAttr = _n->first_attribute("height");
	int height = stoi(pAttr->value());
	pAttr = _n->first_attribute("depth");
	int depth = stoi(pAttr->value());
	//get the position
	xml_node<> *node = _n->first_node("position");
	vec4 camPos;
	getVec4(node, camPos);
	//get the target
	node = _n->first_node("target");
	vec4 camTarget;
	getVec4(node, camTarget);
	//get the up vector
	node = _n->first_node("upvec");
	vec4 upVec;
	getVec4(node, upVec);

	//store the camera info
	if (isVerbose)printf("Camera: FOV: %f\n", fov);
	if (isVerbose)printf("Position: (%f, %f, %f)\n", camPos.r, camPos.g, camPos.b);
	if (isVerbose)printf("Target: (%f, %f, %f)\n", camTarget.r, camTarget.g, camTarget.b);
	if (isVerbose)printf("UpVec: (%f, %f, %f)\n", upVec.r, upVec.g, upVec.b);
	scene->addCamera(camPos, camTarget, upVec, fov, width, height, depth);
	scene->setActiveCameraToLatest();	//set this camera as the active one
	return true;
}

bool XMLFileLoader::processShapes(xml_node<> *_n){
	//if _n is null, then there will be no shapes
	if (_n == NULL){
		printf("No shapes\n");
		return false;
	}
	//for each frame
	for (xml_node<> *node = _n->first_node(); node; node = node->next_sibling()){
		processShape(node);
	}
	return true;
}

bool XMLFileLoader::processShape(xml_node<> *_n){
	if (_n == NULL){
		printf("No shape\n");
		return false;
	}
	//get the position
	xml_node<> *node = _n->first_node("position");
	vec4 position;
	getVec4(node, position);
	//get the shape rotation
	node = _n->first_node("rotation");
	Rotate4 rotation = Rotate4(0);
	if (node != NULL){
		getRotate4(node, rotation);
	}
	//get the shader's name
	xml_attribute<> *pAttr = _n->first_attribute("shader");
	string shaderName = pAttr->value();
	//get the shape type
	pAttr = _n->first_attribute("type");
	string shapeType = pAttr->value();
	if (shapeType == "tesseract"){
		//get the side length
		node = _n->first_node("sidelength");
		float sideLength = stof(node->value());
		scene->addTesseract(position, rotation, sideLength, shaderName);
	}
	else if (shapeType == "hyperplane"){
		//get the up vector
		node = _n->first_node("upvec");
		vec4 upVec;
		getVec4(node, upVec);
		//TODO: add a hyperplane
	}
	/*else if (shapeType == "plane"){
		//get the up vec
		node = _n->first_node("upvec");
		vec4 upVec;
		getVec4(node, upVec);
		//add a plane
		if (isVerbose)printf("Plane: \"%s\"\n", shaderName.c_str());
		if (isVerbose)printf("Upvec: (%f, %f, %f)\n", upVec.r, upVec.g, upVec.b);
		scene->addPlane(position, upVec, shaderName);
	}*/
	/*
	else if (shapeType == "obj"){
		//get the file path
		pAttr = _n->first_attribute("src");
		string filepath = pAttr->value();
		//add a triangle mesh
		if (isVerbose)printf("OBJ: \"%s\", file: %s\n", shaderName.c_str(), filepath.c_str());
		scene->addOBJ(position, filepath, shaderName);
	}*/

	if (isVerbose)printf("Position: (%f, %f, %f)\n", position.r, position.g, position.b);
	return true;
}

bool XMLFileLoader::processLights(xml_node<> *_n){
	//if _n is null, then there will be no lights
	if (_n == NULL){
		printf("No lights\n");
		return false;
	}
	//for each frame
	for (xml_node<> *node = _n->first_node(); node; node = node->next_sibling()){
		xml_attribute<> *pAttr = node->first_attribute("type");
		string lightType = pAttr->value();
		if (lightType == "point"){
			processPointLight(node);
		}
		
	}
	return true;
}


bool XMLFileLoader::processPointLight(xml_node<> *_n){
	if (_n == NULL){
		return false;
	}
	//get the intensity
	xml_attribute<> *pAttr = _n->first_attribute("intensity");
	float intensity = stof(pAttr->value());
	//get the position
	xml_node<> *node = _n->first_node("position");
	vec4 lightPos;
	getVec4(node, lightPos);
	//get the color
	node = _n->first_node("color");
	vec4 lightColor;
	getVec4(node, lightColor);

	//store the light
	if (isVerbose)printf("Point light: intensity: %f\n", intensity);
	if (isVerbose)printf("Position: (%f, %f, %f)\n", lightPos.r, lightPos.g, lightPos.b);
	if (isVerbose)printf("Color: (%f, %f, %f)\n", lightColor.r, lightColor.g, lightColor.b);
	scene->addLight(lightPos, lightColor, intensity);
	return true;
}


bool XMLFileLoader::getVec4(xml_node<> *_n, vec4 &_v){
	xml_node<> *node = _n->first_node("vec4");
	if (node == NULL){
		return false;
	}
	xml_node<> *vecNode = node->first_node();
	_v.x = stof(vecNode->value());
	_v.y = stof(vecNode->next_sibling()->value());
	vecNode = vecNode->next_sibling();
	_v.z = stof(vecNode->next_sibling()->value());
	vecNode = vecNode->next_sibling();
	_v.a = stof(vecNode->next_sibling()->value());
	return true;
}

bool XMLFileLoader::getRotate4(xml_node<> *_n, Rotate4 &_r){
	xml_node<> *node = _n->first_node("rot4");
	if (node == NULL){
		return false;
	}
	float xy, xz, xw, yz, yw, zw;
	xml_node<> *rotNode = node->first_node("xy");
	xy = stof(rotNode->value());
	rotNode = node->first_node("xz");
	xz = stof(rotNode->value());
	rotNode = node->first_node("xw");
	xw = stof(rotNode->value());
	rotNode = node->first_node("yz");
	yz = stof(rotNode->value());
	rotNode = node->first_node("yw");
	yw = stof(rotNode->value());
	rotNode = node->first_node("zw");
	zw = stof(rotNode->value());
	_r.setRot(xy, xz, xw, yz, yw, zw);
}
